///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.gaugesClasses
{
  import ilog.gauges.TickItem;

  /**
   * The <code>LinearScale</code> class maps numeric values evenly
   * between a minimum and a maximum value along a gauge scale.
   * If no <code>majorTickInterval</code> is specified, the scale automatically computes
   * the <code>majorTickInterval</code> to have ten major intervals.
   * If no <code>minorTickInterval</code> is specified, the scale automatically computes
   * the <code>minorTickInterval</code> to have three minor ticks between two major ticks.
   *
   * @mxml
   *  
   * <p>The <code>&lt;LinearScale&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;LinearScale
   *   <strong>Properties</strong>
   *   majorTickInterval="NaN"
   *   minorTickInterval="NaN"
   *   snapInterval="NaN" 
   * /&gt;
   * </pre>
   *  
   * @see ilog.gauges.circular.CircularLinearScale
   * @see ilog.gauges.rectangular.RectangularLinearScale 
   */
  public class LinearScale extends NumericScale
  {
    /**
     * Constructor
     */
    public function LinearScale()
    {
      super();      
    }

    /**
     *  @private
     */
    private var _majorTickInterval:Number = NaN;
    
    [Inspectable(category="Gauge")]       

    /**
     * The interval between two major ticks. If it is <code>NaN</code>, the major
     * tick interval is automatically computed so that ten ticks appear on the 
     * scale.
     * 
     * @default NaN
     */     
    public function get majorTickInterval():Number
    {
      return _majorTickInterval;
    }

    /**
     *  @private
     */
    public function set majorTickInterval(value:Number):void
    {
      _majorTickInterval = value;
      _computedMajorTickInterval = value;
      invalidateTicks();
    }

    /**
     *  @private
     */
    private var _minorTickInterval:Number = NaN;

    [Inspectable(category="Gauge")]   

    /**
     * The interval between two minor ticks. If it is <code>NaN</code>, the minor
     * tick interval is automatically computed so that three minor ticks appear  
     * between two major ticks.
     * 
     * @see #majorTickInterval
     * 
     * @default NaN
     */     
    public function get minorTickInterval():Number
    {
      return _minorTickInterval;
    }

    /**
     *  @private
     */
    public function set minorTickInterval(value:Number):void
    {
      _minorTickInterval = value;
      _computedMinorTickInterval = value;
      invalidateTicks();
    }

    /**
     *  @private
     */
    private var _computedMajorTickInterval:Number = NaN; 

    /**
     *  @private
     */
    private var _computedMinorTickInterval:Number = NaN;
    
    /**
     *  @private
     */    
    private function get computedMajorTickInterval():Number
    {
      if (isNaN(_computedMajorTickInterval))
        _computedMajorTickInterval = (maximum - minimum) / 10;        
      return _computedMajorTickInterval;
    }

    /**
     *  @private
     */
    private function get computedMinorTickInterval():Number
    {
      if (isNaN(_computedMinorTickInterval)) 
        _computedMinorTickInterval = computedMajorTickInterval / 3;
      return _computedMinorTickInterval;
    }

    /**
     *  @private
     */
    protected override function buildMinorTickItems():Array
    {
      var mt:Array = majorTickItems;         
      var minorTickCache:Array = [];
      if (maximum > minimum) {
        var majorTickCount:int = Math.floor((maximum - minimum) / computedMajorTickInterval) + 1;      
        var minorTickCount:int = Math.floor(computedMajorTickInterval / computedMinorTickInterval);
        var data:TickItem;
        for (var i:int = 0; i < majorTickCount - 1; i++) {
          for (var j:int = 1; j < minorTickCount; j++) {
            data = new TickItem(this);
            data.isMinor = true;
            data.value = mt[i].value + j * computedMinorTickInterval;
            data.position = (Number(data.value) - minimum) / (maximum - minimum);
            minorTickCache.push(data);
          }       
        }
      }
      return minorTickCache;
    } 

    /**
     *  @private
     */
    protected override function buildMajorTickItems():Array
    {
      var majorTickCache:Array = [];
      if (maximum > minimum) {
        var majorTickCount:int = Math.floor((maximum - minimum) / computedMajorTickInterval) + 1;
        var data:TickItem;
        for (var i:int = 0; i < majorTickCount; i++) {
          data = new TickItem(this);
          data.isMinor = false;
          data.value = minimum + i * computedMajorTickInterval;
          data.position = (Number(data.value) - minimum) / (maximum - minimum);
          majorTickCache.push(data); 
        }        
      }
      return majorTickCache;
   }
        
    /**
     * @private
     */
    private var _snapInterval:Number = NaN;

    [Inspectable(category="Gauge", defaultValue="NaN")]

    /**
     * Specifies the increment value to be used to snap values on this
     * scale during user interaction. For example, if <code>snapInterval</code> 
     * is 2, the <code>minimum</code> value is 0 and the <code>maximum</code> 
     * value is 10, the possible values returned by the scale for 
     * interactions will be 0, 2, 4, 6, 8, and 10.
     * A value of <code>NaN</code> or <code>0</code>, means that no snapping 
     * takes place.
     *
     * @default NaN
     * @ilog.reviewed SH April 24 08
     */
    public function get snapInterval():Number
    {
      return _snapInterval;
    }

    /**
     * @private
     */
    public function set snapInterval(value:Number):void
    {
      _snapInterval = value;
      var parts:Array = (new String(1 + value)).split(".");
      if (parts.length == 2)
        _snapIntervalPrecision = parts[1].length;
      else
        _snapIntervalPrecision = -1;      
    }        
    
    private var _snapIntervalPrecision:int = -1;
    
    /**
     * The precision of the snap interval. This is <code>-1</code> if the
     * snap interval is an integer value, otherwise it is the number
     * of decimals in the snap interval.
     * 
     * @see #snapInterval
     */
    protected function get snapIntervalPrecision():int
    {
      return _snapIntervalPrecision;
    }
  }
}
